#include "User.h"
#include "SSD1306_OLED.h"

#define max(a, b) (a > b ? a : b)

#define delay(n) delay_ms(n)

HardwareUART Serial(UART_1);

SSD1306_GUI display(SPI_2, B12, B11, B3);

#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
const unsigned char logo_bmp[] =
    {
        0x00, 0xC0,  // B00000000, B11000000,
        0x01, 0xC0,  // B00000001, B11000000,
        0x01, 0xC0,  // B00000001, B11000000,
        0x03, 0xE0,  // B00000011, B11100000,
        0xF3, 0xE0,  // B11110011, B11100000,
        0xFE, 0xF8,  // B11111110, B11111000,
        0x7E, 0xFF,  // B01111110, B11111111,
        0x33, 0x9F,  // B00110011, B10011111,
        0x1F, 0xFC,  // B00011111, B11111100,
        0x0D, 0x70,  // B00001101, B01110000,
        0x1B, 0xA0,  // B00011011, B10100000,
        0x3F, 0xE0,  // B00111111, B11100000,
        0x3F, 0xf0,  // B00111111, B11110000,
        0x7C, 0xF0,  // B01111100, B11110000,
        0x70, 0x70,  // B01110000, B01110000,
        0x00, 0x30}; // B00000000, B00110000};

void testdrawline(), // Draw many lines

    testdrawrect(), // Draw rectangles (outlines)

    testfillrect(), // Draw rectangles (filled)

    testdrawcircle(), // Draw circles (outlines)

    testfillcircle(), // Draw circles (filled)

    testdrawroundrect(), // Draw rounded rectangles (outlines)

    testfillroundrect(), // Draw rounded rectangles (filled)

    testdrawtriangle(), // Draw triangles (outlines)

    testfilltriangle(), // Draw triangles (filled)

    testdrawchar(), // Draw characters of the default font

    testdrawstyles(), // Draw 'stylized' characters

    testdrawbitmap(); // Draw a small bitmap image

void setup()
{
    Serial.begin(9600);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if (!display.begin())
    { // Address 0x3D for 128x64
        Serial.println("SSD1306 allocation failed");
        for (;;)
            ; // Don't proceed, loop forever
    }

    // Show initial display buffer contents on the screen --
    // the library initializes this with an Adafruit splash screen.
    display.show();
    delay(2000); // Pause for 2 seconds

    // Clear the buffer
    display.clear();

    // Draw a single pixel in white
    display.drawPixel(10, 10, SSD1306_WHITE);

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.show();
    delay(2000);
    // display.display() is NOT necessary after every single drawing command,
    // unless that's what you want...rather, you can batch up a bunch of
    // drawing operations and then update the screen all at once by calling
    // display.display(). These examples demonstrate both approaches...

    testdrawline(); // Draw many lines

    testdrawrect(); // Draw rectangles (outlines)

    testfillrect(); // Draw rectangles (filled)

    testdrawcircle(); // Draw circles (outlines)

    testfillcircle(); // Draw circles (filled)

    testdrawroundrect(); // Draw rounded rectangles (outlines)

    testfillroundrect(); // Draw rounded rectangles (filled)

    testdrawtriangle(); // Draw triangles (outlines)

    testfilltriangle(); // Draw triangles (filled)

    testdrawchar(); // Draw characters of the default font

    testdrawstyles(); // Draw 'stylized' characters

    testdrawbitmap(); // Draw a small bitmap image

    // Invert and restore display, pausing in-between
    display.invertDisplay(true);
    delay(1000);
    display.invertDisplay(false);
    delay(1000);

    // testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}

void loop()
{
}

void testdrawline()
{
    int16_t i;

    display.clear(); // Clear display buffer

    for (i = 0; i < display.width(); i += 4)
    {
        display.drawLine(0, 0, i, display.height() - 1, SSD1306_WHITE);
        display.show(); // Update screen with each newly-drawn line
        delay(1);
    }
    for (i = 0; i < display.height(); i += 4)
    {
        display.drawLine(0, 0, display.width() - 1, i, SSD1306_WHITE);
        display.show();
        delay(1);
    }
    delay(250);

    display.clear();

    for (i = 0; i < display.width(); i += 4)
    {
        display.drawLine(0, display.height() - 1, i, 0, SSD1306_WHITE);
        display.show();
        delay(1);
    }
    for (i = display.height() - 1; i >= 0; i -= 4)
    {
        display.drawLine(0, display.height() - 1, display.width() - 1, i, SSD1306_WHITE);
        display.show();
        delay(1);
    }
    delay(250);

    display.clear();

    for (i = display.width() - 1; i >= 0; i -= 4)
    {
        display.drawLine(display.width() - 1, display.height() - 1, i, 0, SSD1306_WHITE);
        display.show();
        delay(1);
    }
    for (i = display.height() - 1; i >= 0; i -= 4)
    {
        display.drawLine(display.width() - 1, display.height() - 1, 0, i, SSD1306_WHITE);
        display.show();
        delay(1);
    }
    delay(250);

    display.clear();

    for (i = 0; i < display.height(); i += 4)
    {
        display.drawLine(display.width() - 1, 0, 0, i, SSD1306_WHITE);
        display.show();
        delay(1);
    }
    for (i = 0; i < display.width(); i += 4)
    {
        display.drawLine(display.width() - 1, 0, i, display.height() - 1, SSD1306_WHITE);
        display.show();
        delay(1);
    }

    delay(2000); // Pause for 2 seconds
}

void testdrawrect(void)
{
    display.clear();

    for (int16_t i = 0; i < display.height() / 2; i += 2)
    {
        display.drawRect(i, i, display.width() - 2 * i, display.height() - 2 * i, SSD1306_WHITE);
        display.show(); // Update screen with each newly-drawn rectangle
        delay(1);
    }

    delay(2000);
}

void testfillrect(void)
{
    display.clear();

    for (int16_t i = 0; i < display.height() / 2; i += 3)
    {
        // The INVERSE color is used so rectangles alternate white/black
        display.fillRect(i, i, display.width() - i * 2, display.height() - i * 2, SSD1306_INVERSE);
        display.show(); // Update screen with each newly-drawn rectangle
        delay(1);
    }

    delay(2000);
}

void testdrawcircle(void)
{
    display.clear();

    for (int16_t i = 0; i < max(display.width(), display.height()) / 2; i += 2)
    {
        display.drawCircle(display.width() / 2, display.height() / 2, i, SSD1306_WHITE);
        display.show();
        delay(1);
    }

    delay(2000);
}

void testfillcircle(void)
{
    display.clear();

    for (int16_t i = max(display.width(), display.height()) / 2; i > 0; i -= 3)
    {
        // The INVERSE color is used so circles alternate white/black
        display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
        display.show(); // Update screen with each newly-drawn circle
        delay(1);
    }

    delay(2000);
}

void testdrawroundrect(void)
{
    display.clear();

    for (int16_t i = 0; i < display.height() / 2 - 2; i += 2)
    {
        display.drawRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i,
                              display.height() / 4, SSD1306_WHITE);
        display.show();
        delay(1);
    }

    delay(2000);
}

void testfillroundrect(void)
{
    display.clear();

    for (int16_t i = 0; i < display.height() / 2 - 2; i += 2)
    {
        // The INVERSE color is used so round-rects alternate white/black
        display.fillRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i,
                              display.height() / 4, SSD1306_INVERSE);
        display.show();
        delay(1);
    }

    delay(2000);
}

void testdrawtriangle(void)
{
    display.clear();

    for (int16_t i = 0; i < max(display.width(), display.height()) / 2; i += 5)
    {
        display.drawTriangle(
            display.width() / 2, display.height() / 2 - i,
            display.width() / 2 - i, display.height() / 2 + i,
            display.width() / 2 + i, display.height() / 2 + i, SSD1306_WHITE);
        display.show();
        delay(1);
    }

    delay(2000);
}

void testfilltriangle(void)
{
    display.clear();

    for (int16_t i = max(display.width(), display.height()) / 2; i > 0; i -= 5)
    {
        // The INVERSE color is used so triangles alternate white/black
        display.fillTriangle(
            display.width() / 2, display.height() / 2 - i,
            display.width() / 2 - i, display.height() / 2 + i,
            display.width() / 2 + i, display.height() / 2 + i, SSD1306_INVERSE);
        display.show();
        delay(1);
    }

    delay(2000);
}

void testdrawchar(void)
{
    display.clear();

    display.setTextSize(1);              // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);             // Start at top-left corner
    display.cp437(true);                 // Use full 256 char 'Code Page 437' font

    // Not all the characters will fit on the display. This is normal.
    // Library will draw what it can and the rest will be clipped.
    for (int16_t i = 0; i < 256; i++)
    {
        if (i == '\n')
            display.write(' ');
        else
            display.write(i);
    }

    display.show();
    delay(2000);
}

void testdrawstyles(void)
{
    display.clear();

    display.setTextSize(1);              // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);             // Start at top-left corner
    display.println("Hello, world!");

    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
    display.println(3.141592);

    display.setTextSize(2); // Draw 2X-scale text
    display.setTextColor(SSD1306_WHITE);
    display.print("0x");
    display.println(0xDEADBEEF, HEX);

    display.show();
    delay(2000);
}

void testdrawbitmap(void)
{
    display.clear();

    display.drawBitmap(
        (display.width() - LOGO_WIDTH) / 2,
        (display.height() - LOGO_HEIGHT) / 2,
        logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
    display.show();
    delay(1000);
}
